using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Diagnostics;
using SmartSite.Network;

namespace SmartSite.Frame
{
    [Serializable]
    public class RestoreFrameDictionary : Dictionary<FrameTypeEnum, Func<byte[], BaseFrame>>
    {
        public event EventHandler<RestoredDataEventArgs> Restored;
        public event EventHandler<SocketStateEventArgs> ProtocolVersionError;
        public event EventHandler<SocketStateEventArgs> FrameTypeError;
        public event EventHandler<SocketStateEventArgs> CrcError;
        public event EventHandler<SocketStateEventArgs> UnknowFrameError;

        public RestoreFrameDictionary() { }

        protected RestoreFrameDictionary(
            SerializationInfo serializationInfo,
            StreamingContext streamingContext)
        {
            throw new NotImplementedException();
        }

        public void Restore(SocketStateEventArgs state)
        {
            if (state == null)
                throw new ArgumentNullException(nameof(state));

            Restore(state, this[FrameTypeEnum.NullFrameType]);
        }

        void Restore(SocketStateEventArgs state, Func<byte[], BaseFrame> getFrame)
        {
            if (state == null)
                throw new ArgumentNullException(nameof(state));

            Debug.Assert(state != null);

            var frame = getFrame(state.BufferClone());
            if (state.BufferLength >= frame.BuffertLength)
            {

                if (!IsProtocolVersionCorrect(frame.ProtocolVersion)) OnProtocolVersionError(state);

                else if (!this.ContainsKey(frame.FrameType)) OnFrameTypeError(state);

                else if (!IsCrcCorrect(frame)) OnCrcError(state);

                else if (IsOtherFrame(frame.Key, frame.FrameType))
                    Restore(state, this[frame.FrameType]);
                else
                {
                    Debug.Assert(frame.Key == frame.FrameType);
                    Debug.Assert(frame != null);
                    OnRestored(frame, state);
                }
            }
            else OnUnknowFrameError(state);
        }

        static bool IsProtocolVersionCorrect(int protocolVersion)
            => protocolVersion == BaseFrameDataEnum.ProtocolVersion;
        static bool IsCrcCorrect(BaseFrame frame)
            => IsOtherFrame(frame.Key, frame.FrameType) || frame.CRC == frame.CalculationCRC();
        static bool IsOtherFrame(FrameTypeEnum frameKey, FrameTypeEnum frameType) => frameKey != frameType;

        protected virtual void OnRestored(BaseFrame frame, SocketStateEventArgs socketState)
        {
            Restored?.Invoke(this, new RestoredDataEventArgs(frame, socketState));
            frame.OnRestored(socketState);
        }

        protected virtual void OnUnknowFrameError(SocketStateEventArgs state)
            => UnknowFrameError?.Invoke(this, state);

        protected virtual void OnProtocolVersionError(SocketStateEventArgs state)
            => ProtocolVersionError?.Invoke(this, state);

        protected virtual void OnFrameTypeError(SocketStateEventArgs state)
            => FrameTypeError?.Invoke(this, state);

        protected virtual void OnCrcError(SocketStateEventArgs state) => CrcError?.Invoke(this, state);
    }
}
